/*
 ------------------------------------------------------------------------------
 Copyright (C) 2009-2011 Eternal Games.

 This file is part of the Eternal Tech source code.

 The Eternal Tech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Eternal Tech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Eternal Tech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// r_batch.cpp - Geometry batching for back-end
//


#include "r_local.h"


/*
 ==============================================================================

    BATCH SETUP

 ==============================================================================
*/


/*
 ==================
 RB_CheckOverflow
 ==================
*/
void RB_CheckOverflow (int numIndices, int numVertices, int maxIndices, int maxVertices)
{
	if (backEnd.numIndices + numIndices <= maxIndices && backEnd.numVertices + numVertices <= maxVertices)
		return;

	RB_RenderBatch();

	if (numIndices > maxIndices)
		Com_Error(ERR_DROP, "RB_CheckOverflow: indices > MAX (%i > %i)", numIndices, maxIndices);
	if (numVertices > maxVertices)
		Com_Error(ERR_DROP, "RB_CheckOverflow: vertices > MAX (%i > %i)", numVertices, maxVertices);

	RB_SetupBatch(backEnd.material, backEnd.entity, backEnd.stencilShadow, backEnd.shadowCaps);
}

/*
 ==================
 RB_SetupBatch
 ==================
*/
void RB_SetupBatch (material_t *material, renderEntity_t *entity, bool stencilShadow, bool shadowCaps)
{
	// Set the batch state
	backEnd.material = material;
	backEnd.entity = entity;

	backEnd.stencilShadow = stencilShadow;
	backEnd.shadowCaps = shadowCaps;
}

/*
 ==================
 RB_RenderBatch
 ==================
*/
void RB_RenderBatch ()
{
	if (!backEnd.numIndices || !backEnd.numVertices)
		return;

	// Render the batch
	backEnd.renderBatch();

	// Check for errors
	if (!r_ignoreGLErrors->integerValue)
		GL_CheckForErrors();

	// Clear the arrays
	backEnd.numIndices = 0;
	backEnd.numVertices = 0;
}


/*
 ==============================================================================

    MESH RENDERING

 ==============================================================================
*/


/*
 ==================
 RB_BatchSurface
 ==================
*/
static void RB_BatchSurface (meshData_t *data)
{
	surface_t		*surface = (surface_t*)data;
	surfTriangle_t	*triangle;
	surfVertex_t	*vertex;
	index_t			*indices;
	vertexArray_t	*vertices;
	int				i;

	// Check for overflow
	RB_CheckOverflow(surface->numTriangles * 3, surface->numVertices, MAX_INDICES, MAX_VERTICES);

	// Set up indices
	indices = backEnd.indices + backEnd.numIndices;

	for (i = 0, triangle = surface->triangles; i < surface->numTriangles; i++, triangle++)
	{
		indices[0] = backEnd.numVertices + triangle->index[0];
		indices[1] = backEnd.numVertices + triangle->index[1];
		indices[2] = backEnd.numVertices + triangle->index[2];

		indices += 3;
	}

	backEnd.numIndices += surface->numTriangles * 3;

	// Set up vertices
	vertices = backEnd.vertices + backEnd.numVertices;

	for (i = 0, vertex = surface->vertices; i < surface->numVertices; i++, vertex++)
	{
		vertices->xyz[0] = vertex->xyz[0];
		vertices->xyz[1] = vertex->xyz[1];
		vertices->xyz[2] = vertex->xyz[2];
		vertices->normal[0] = vertex->normal[0];
		vertices->normal[1] = vertex->normal[1];
		vertices->normal[2] = vertex->normal[2];
		vertices->tangents[0][0] = vertex->tangents[0][0];
		vertices->tangents[0][1] = vertex->tangents[0][1];
		vertices->tangents[0][2] = vertex->tangents[0][2];
		vertices->tangents[1][0] = vertex->tangents[1][0];
		vertices->tangents[1][1] = vertex->tangents[1][1];
		vertices->tangents[1][2] = vertex->tangents[1][2];
		vertices->st[0] = vertex->st[0];
		vertices->st[1] = vertex->st[1];
		vertices->color[0] = vertex->color[0];
		vertices->color[1] = vertex->color[1];
		vertices->color[2] = vertex->color[2];
		vertices->color[3] = vertex->color[3];

		vertices++;
	}

	backEnd.numVertices += surface->numVertices;
}

/*
 ==================
 RB_BatchAliasModel
 ==================
*/
static void RB_BatchAliasModel (meshData_t *data)
{
	mdlSurface_t	*surface = (mdlSurface_t*)data;
	mdlTriangle_t	*triangle;
	mdlSt_t			*st;
	mdlXyzNormal_t	*curXyzNormal, *oldXyzNormal;
	float			backLerp;
	index_t			*indices;
	vertexArray_t	*vertices;
	int				i;

	// Interpolate frames
	curXyzNormal = surface->xyzNormals + surface->numVertices * backEnd.entity->frame;
	oldXyzNormal = surface->xyzNormals + surface->numVertices * backEnd.entity->oldFrame;

	if (backEnd.entity->frame == backEnd.entity->oldFrame)
		backLerp = 0.0;
	else
		backLerp = backEnd.entity->backLerp;

	// Check for overflow
	RB_CheckOverflow(surface->numTriangles * 3, surface->numVertices, MAX_INDICES, MAX_VERTICES);

	// Set up indices
	indices = backEnd.indices + backEnd.numIndices;

	for (i = 0, triangle = surface->triangles; i < surface->numTriangles; i++, triangle++)
	{
		indices[0] = backEnd.numVertices + triangle->index[0];
		indices[1] = backEnd.numVertices + triangle->index[1];
		indices[2] = backEnd.numVertices + triangle->index[2];

		indices += 3;
	}

	backEnd.numIndices += surface->numTriangles * 3;

	// Set up vertices
	vertices = backEnd.vertices + backEnd.numVertices;

	if (backLerp == 0.0)
	{
		// Optimized case
		for (i = 0, st = surface->st; i < surface->numVertices; i++, curXyzNormal++, st++)
		{
			vertices->xyz[0] = curXyzNormal->xyz[0];
			vertices->xyz[1] = curXyzNormal->xyz[1];
			vertices->xyz[2] = curXyzNormal->xyz[2];
			vertices->normal[0] = curXyzNormal->normal[0];
			vertices->normal[1] = curXyzNormal->normal[1];
			vertices->normal[2] = curXyzNormal->normal[2];
			vertices->tangents[0][0] = curXyzNormal->tangents[0][0];
			vertices->tangents[0][1] = curXyzNormal->tangents[0][1];
			vertices->tangents[0][2] = curXyzNormal->tangents[0][2];
			vertices->tangents[1][0] = curXyzNormal->tangents[1][0];
			vertices->tangents[1][1] = curXyzNormal->tangents[1][1];
			vertices->tangents[1][2] = curXyzNormal->tangents[1][2];
			vertices->st[0] = st->st[0];
			vertices->st[1] = st->st[1];
			vertices->color[0] = 255;
			vertices->color[1] = 255;
			vertices->color[2] = 255;
			vertices->color[3] = 255;

			VectorScale(vertices->xyz, backEnd.entity->scale, vertices->xyz);

			vertices++;
		}
	}
	else
	{
		// General case
		for (i = 0, st = surface->st; i < surface->numVertices; i++, curXyzNormal++, oldXyzNormal++, st++)
		{
			vertices->xyz[0] = curXyzNormal->xyz[0] + (oldXyzNormal->xyz[0] - curXyzNormal->xyz[0]) * backLerp;
			vertices->xyz[1] = curXyzNormal->xyz[1] + (oldXyzNormal->xyz[1] - curXyzNormal->xyz[1]) * backLerp;
			vertices->xyz[2] = curXyzNormal->xyz[2] + (oldXyzNormal->xyz[2] - curXyzNormal->xyz[2]) * backLerp;
			vertices->normal[0] = curXyzNormal->normal[0] + (oldXyzNormal->normal[0] - curXyzNormal->normal[0]) * backLerp;
			vertices->normal[1] = curXyzNormal->normal[1] + (oldXyzNormal->normal[1] - curXyzNormal->normal[1]) * backLerp;
			vertices->normal[2] = curXyzNormal->normal[2] + (oldXyzNormal->normal[2] - curXyzNormal->normal[2]) * backLerp;
			vertices->tangents[0][0] = curXyzNormal->tangents[0][0] + (oldXyzNormal->tangents[0][0] - curXyzNormal->tangents[0][0]) * backLerp;
			vertices->tangents[0][1] = curXyzNormal->tangents[0][1] + (oldXyzNormal->tangents[0][1] - curXyzNormal->tangents[0][1]) * backLerp;
			vertices->tangents[0][2] = curXyzNormal->tangents[0][2] + (oldXyzNormal->tangents[0][2] - curXyzNormal->tangents[0][2]) * backLerp;
			vertices->tangents[1][0] = curXyzNormal->tangents[1][0] + (oldXyzNormal->tangents[1][0] - curXyzNormal->tangents[1][0]) * backLerp;
			vertices->tangents[1][1] = curXyzNormal->tangents[1][1] + (oldXyzNormal->tangents[1][1] - curXyzNormal->tangents[1][1]) * backLerp;
			vertices->tangents[1][2] = curXyzNormal->tangents[1][2] + (oldXyzNormal->tangents[1][2] - curXyzNormal->tangents[1][2]) * backLerp;
			vertices->st[0] = st->st[0];
			vertices->st[1] = st->st[1];
			vertices->color[0] = 255;
			vertices->color[1] = 255;
			vertices->color[2] = 255;
			vertices->color[3] = 255;

			VectorNormalizeFast(vertices->normal);
			VectorNormalizeFast(vertices->tangents[0]);
			VectorNormalizeFast(vertices->tangents[1]);
		
			VectorScale(vertices->xyz, backEnd.entity->scale, vertices->xyz);

			vertices++;
		}
	}

	backEnd.numVertices += surface->numVertices;
}

/*
 ==================
 RB_BatchParticle
 ==================
*/
static void RB_BatchParticle (meshData_t *data)
{
	renderParticle_t	 *poly = (renderParticle_t *)data;
	renderPolyVertex_t   *vertex;
	index_t				 *indices;
	vertexArray_t		 *vertices;
	int					 i;

	// Check for overflow
	RB_CheckOverflow((poly->numVertices-2) * 3, poly->numVertices, MAX_INDICES, MAX_VERTICES);

	// Set up indices
	indices = backEnd.indices + backEnd.numIndices;

	for (i = 2; i < poly->numVertices; i++)
	{
		indices[0] = backEnd.numVertices + 0;
		indices[1] = backEnd.numVertices + i-1;
		indices[2] = backEnd.numVertices + i;

		indices += 3;
	}

	backEnd.numIndices += (poly->numVertices-2) * 3;

	// Set up vertices
	vertices = backEnd.vertices + backEnd.numVertices;

	for (i = 0, vertex = poly->vertices; i < poly->numVertices; i++, vertex++)
	{
		vertices->xyz[0] = vertex->xyz[0];
		vertices->xyz[1] = vertex->xyz[1];
		vertices->xyz[2] = vertex->xyz[2];
		vertices->normal[0] = vertex->normal[0];
		vertices->normal[1] = vertex->normal[1];
		vertices->normal[2] = vertex->normal[2];
		vertices->st[0] = vertex->st[0];
		vertices->st[1] = vertex->st[1];
		vertices->color[0] = vertex->color[0];
		vertices->color[1] = vertex->color[1];
		vertices->color[2] = vertex->color[2];
		vertices->color[3] = vertex->color[3];

		vertices++;
	}

	backEnd.numVertices += poly->numVertices;
}

// TODO!!!
static void RB_BatchNull (meshData_t *data)
{
}

/*
 ==================
 RB_BatchMesh
 ==================
*/
void RB_BatchMesh (meshType_t type, meshData_t *data)
{
	switch (type){
	case MESH_SURFACE:
		RB_BatchSurface(data);
		break;
	case MESH_ALIASMODEL:
		RB_BatchAliasModel(data);
		break;
	case MESH_SPRITE:
		//RB_BatchSprite(data);
		break;
	case MESH_BEAM:
		//RB_BatchBeam(data);
		break;
	case MESH_DECAL:
		//RB_BatchDecal(data);
		break;
	case MESH_POLY:
		RB_BatchParticle(data);
		break;
	case MESH_NULL:
		RB_BatchNull(data);
		break;
	default:
		Com_Error(ERR_DROP, "RB_BatchMesh: bad type (%i)", type);
	}
}


/*
 ==============================================================================

    SHADOW RENDERING

 ==============================================================================
*/


/*
 ==================
 RB_BatchSurfaceShadow
 ==================
*/
static void RB_BatchSurfaceShadow (meshData_t *data)
{
	index_t				indexRemap[MAX_INDICES];
	surface_t			*surface = (surface_t*)data;
	surfTriangle_t		*triangle;
	surfVertex_t		*vertex;
	index_t				*indices;
	shadowVertexArray_t	*vertices;
	int					i;

	// Check for overflow
	RB_CheckOverflow(surface->numTriangles * 24, surface->numVertices * 2, MAX_SHADOW_INDICES, MAX_SHADOW_VERTICES);

	// Set up vertices
	vertices = backEnd.shadowVertices + backEnd.numVertices;

	for (i = 0, vertex = surface->vertices; i < surface->numVertices; i++, vertex++)
	{
		vertices[0].xyzw[0] = vertex->xyz[0];
		vertices[0].xyzw[1] = vertex->xyz[1];
		vertices[0].xyzw[2] = vertex->xyz[2];
		vertices[0].xyzw[3] = 1.0;

		vertices[1].xyzw[0] = vertex->xyz[0] - backEnd.localParms.lightOrigin[0];
		vertices[1].xyzw[1] = vertex->xyz[1] - backEnd.localParms.lightOrigin[1];
		vertices[1].xyzw[2] = vertex->xyz[2] - backEnd.localParms.lightOrigin[2];
		vertices[1].xyzw[3] = 0.0;

		vertices += 2;

		indexRemap[i] = backEnd.numVertices;
		backEnd.numVertices += 2;
	}

	// Set up indices for silhouette edges
	indices = backEnd.shadowIndices + backEnd.numIndices;

	for (i = 0, triangle = surface->triangles; i < surface->numTriangles; i++, triangle++)
	{
		if (triangle->neighbor[0] < 0)
		{
			indices[0] = indexRemap[triangle->index[1]];
			indices[1] = indexRemap[triangle->index[0]];
			indices[2] = indexRemap[triangle->index[0]] + 1;
			indices[3] = indexRemap[triangle->index[1]];
			indices[4] = indexRemap[triangle->index[0]] + 1;
			indices[5] = indexRemap[triangle->index[1]] + 1;

			indices += 6;

			backEnd.numIndices += 6;
		}

		if (triangle->neighbor[1] < 0)
		{
			indices[0] = indexRemap[triangle->index[2]];
			indices[1] = indexRemap[triangle->index[1]];
			indices[2] = indexRemap[triangle->index[1]] + 1;
			indices[3] = indexRemap[triangle->index[2]];
			indices[4] = indexRemap[triangle->index[1]] + 1;
			indices[5] = indexRemap[triangle->index[2]] + 1;

			indices += 6;

			backEnd.numIndices += 6;
		}

		if (triangle->neighbor[2] < 0)
		{
			indices[0] = indexRemap[triangle->index[0]];
			indices[1] = indexRemap[triangle->index[2]];
			indices[2] = indexRemap[triangle->index[2]] + 1;
			indices[3] = indexRemap[triangle->index[0]];
			indices[4] = indexRemap[triangle->index[2]] + 1;
			indices[5] = indexRemap[triangle->index[0]] + 1;

			indices += 6;

			backEnd.numIndices += 6;
		}
	}

	// Set up indices for front and back caps
	if (backEnd.shadowCaps)
	{
		for (i = 0, triangle = surface->triangles; i < surface->numTriangles; i++, triangle++)
		{
			indices[0] = indexRemap[triangle->index[0]];
			indices[1] = indexRemap[triangle->index[1]];
			indices[2] = indexRemap[triangle->index[2]];
			indices[3] = indexRemap[triangle->index[2]] + 1;
			indices[4] = indexRemap[triangle->index[1]] + 1;
			indices[5] = indexRemap[triangle->index[0]] + 1;

			indices += 6;

			backEnd.numIndices += 6;
		}
	}
}

/*
 ==================
 RB_BatchAliasModelShadow
 ==================
*/
static void RB_BatchAliasModelShadow (meshData_t *data)
{
	bool				triangleFacingLight[MAX_INDICES / 3];
	index_t				indexRemap[MAX_INDICES];
	mdlSurface_t		*surface = (mdlSurface_t*)data;
	mdlTriangle_t		*triangle;
	mdlFacePlane_t		*curFacePlane, *oldFacePlane;
	mdlXyzNormal_t		*curXyzNormal, *oldXyzNormal;
	float				backLerp;
	vec3_t				xyz, normal;
	float				dist;
	index_t				*indices;
	shadowVertexArray_t	*vertices;
	int					i;

	// Interpolate frames
	curFacePlane = surface->facePlanes + surface->numTriangles * backEnd.entity->frame;
	oldFacePlane = surface->facePlanes + surface->numTriangles * backEnd.entity->oldFrame;

	curXyzNormal = surface->xyzNormals + surface->numVertices * backEnd.entity->frame;
	oldXyzNormal = surface->xyzNormals + surface->numVertices * backEnd.entity->oldFrame;

	if (backEnd.entity->frame == backEnd.entity->oldFrame)
		backLerp = 0.0;
	else
		backLerp = backEnd.entity->backLerp;

	// Check for overflow
	RB_CheckOverflow(surface->numTriangles * 24, surface->numVertices * 2, MAX_SHADOW_INDICES, MAX_SHADOW_VERTICES);

	// Set up vertices
	vertices = backEnd.shadowVertices + backEnd.numVertices;

	if (backLerp == 0.0)
	{
		// Optimized case
		for (i = 0; i < surface->numVertices; i++, curXyzNormal++)
		{
			vertices[0].xyzw[0] = curXyzNormal->xyz[0];
			vertices[0].xyzw[1] = curXyzNormal->xyz[1];
			vertices[0].xyzw[2] = curXyzNormal->xyz[2];
			vertices[0].xyzw[3] = 1.0;

			VectorScale(vertices[0].xyzw, backEnd.entity->scale, vertices[0].xyzw);

			vertices[1].xyzw[0] = (curXyzNormal->xyz[0] * backEnd.entity->scale) - backEnd.localParms.lightOrigin[0];
			vertices[1].xyzw[1] = (curXyzNormal->xyz[1] * backEnd.entity->scale) - backEnd.localParms.lightOrigin[1];
			vertices[1].xyzw[2] = (curXyzNormal->xyz[2] * backEnd.entity->scale) - backEnd.localParms.lightOrigin[2];
			vertices[1].xyzw[3] = 0.0;

			vertices += 2;

			indexRemap[i] = backEnd.numVertices;
			backEnd.numVertices += 2;
		}
	}
	else
	{
		// General case
		for (i = 0; i < surface->numVertices; i++, curXyzNormal++, oldXyzNormal++)
		{
			xyz[0] = curXyzNormal->xyz[0] + (oldXyzNormal->xyz[0] - curXyzNormal->xyz[0]) * backLerp;
			xyz[1] = curXyzNormal->xyz[1] + (oldXyzNormal->xyz[1] - curXyzNormal->xyz[1]) * backLerp;
			xyz[2] = curXyzNormal->xyz[2] + (oldXyzNormal->xyz[2] - curXyzNormal->xyz[2]) * backLerp;

			VectorScale(xyz, backEnd.entity->scale, xyz);

			vertices[0].xyzw[0] = xyz[0];
			vertices[0].xyzw[1] = xyz[1];
			vertices[0].xyzw[2] = xyz[2];
			vertices[0].xyzw[3] = 1.0;

			vertices[1].xyzw[0] = xyz[0] - backEnd.localParms.lightOrigin[0];
			vertices[1].xyzw[1] = xyz[1] - backEnd.localParms.lightOrigin[1];
			vertices[1].xyzw[2] = xyz[2] - backEnd.localParms.lightOrigin[2];
			vertices[1].xyzw[3] = 0.0;

			vertices += 2;

			indexRemap[i] = backEnd.numVertices;
			backEnd.numVertices += 2;
		}
	}

	// Find front facing triangles
	if (backLerp == 0.0)
	{
		// Optimized case
		for (i = 0; i < surface->numTriangles; i++, curFacePlane++)
		{
			if (DotProduct(backEnd.localParms.lightOrigin, curFacePlane->normal) - curFacePlane->dist > 0)
				triangleFacingLight[i] = true;
			else
				triangleFacingLight[i] = false;
		}
	}
	else
	{
		// General case
		for (i = 0; i < surface->numTriangles; i++, curFacePlane++, oldFacePlane++)
		{
			normal[0] = curFacePlane->normal[0] + (oldFacePlane->normal[0] - curFacePlane->normal[0]) * backLerp;
			normal[1] = curFacePlane->normal[1] + (oldFacePlane->normal[1] - curFacePlane->normal[1]) * backLerp;
			normal[2] = curFacePlane->normal[2] + (oldFacePlane->normal[2] - curFacePlane->normal[2]) * backLerp;

			dist = curFacePlane->dist + (oldFacePlane->dist - curFacePlane->dist) * backLerp;

			if (DotProduct(backEnd.localParms.lightOrigin, normal) - dist > 0)
				triangleFacingLight[i] = true;
			else
				triangleFacingLight[i] = false;
		}
	}

	// Set up indices for silhouette edges
	indices = backEnd.shadowIndices + backEnd.numIndices;

	for (i = 0, triangle = surface->triangles; i < surface->numTriangles; i++, triangle++)
	{
		if (!triangleFacingLight[i])
			continue;

		if (triangle->neighbor[0] < 0 || !triangleFacingLight[triangle->neighbor[0]])
		{
			indices[0] = indexRemap[triangle->index[1]];
			indices[1] = indexRemap[triangle->index[0]];
			indices[2] = indexRemap[triangle->index[0]] + 1;
			indices[3] = indexRemap[triangle->index[1]];
			indices[4] = indexRemap[triangle->index[0]] + 1;
			indices[5] = indexRemap[triangle->index[1]] + 1;

			indices += 6;

			backEnd.numIndices += 6;
		}

		if (triangle->neighbor[1] < 0 || !triangleFacingLight[triangle->neighbor[1]])
		{
			indices[0] = indexRemap[triangle->index[2]];
			indices[1] = indexRemap[triangle->index[1]];
			indices[2] = indexRemap[triangle->index[1]] + 1;
			indices[3] = indexRemap[triangle->index[2]];
			indices[4] = indexRemap[triangle->index[1]] + 1;
			indices[5] = indexRemap[triangle->index[2]] + 1;

			indices += 6;

			backEnd.numIndices += 6;
		}

		if (triangle->neighbor[2] < 0 || !triangleFacingLight[triangle->neighbor[2]])
		{
			indices[0] = indexRemap[triangle->index[0]];
			indices[1] = indexRemap[triangle->index[2]];
			indices[2] = indexRemap[triangle->index[2]] + 1;
			indices[3] = indexRemap[triangle->index[0]];
			indices[4] = indexRemap[triangle->index[2]] + 1;
			indices[5] = indexRemap[triangle->index[0]] + 1;

			indices += 6;

			backEnd.numIndices += 6;
		}
	}

	// Set up indices for front and back caps
	if (backEnd.shadowCaps)
	{
		for (i = 0, triangle = surface->triangles; i < surface->numTriangles; i++, triangle++)
		{
			if (!triangleFacingLight[i])
				continue;

			indices[0] = indexRemap[triangle->index[0]];
			indices[1] = indexRemap[triangle->index[1]];
			indices[2] = indexRemap[triangle->index[2]];
			indices[3] = indexRemap[triangle->index[2]] + 1;
			indices[4] = indexRemap[triangle->index[1]] + 1;
			indices[5] = indexRemap[triangle->index[0]] + 1;

			indices += 6;

			backEnd.numIndices += 6;
		}
	}
}

/*
 ==================
 RB_BatchMeshShadow

 TODO: Add MESH_NULL?
 ==================
*/
void RB_BatchMeshShadow (meshType_t type, meshData_t *data)
{
	switch (type){
	case MESH_SURFACE:
		RB_BatchSurfaceShadow(data);
		break;
	case MESH_ALIASMODEL:
		RB_BatchAliasModelShadow(data);
		break;
	default:
		Com_Error(ERR_DROP, "RB_BatchMeshShadow: bad type (%i)", type);
	}
}